package com.zhoutao.blog.seven.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.zhoutao.blog.seven.dao.model.ArticleDao;
import com.zhoutao.blog.seven.dao.model.BlogCategoryDao;
import com.zhoutao.blog.seven.dao.model.TagDao;
import com.zhoutao.blog.seven.dao.repository.ArticleRepository;
import com.zhoutao.blog.seven.dao.repository.BlogCategoryRepository;
import com.zhoutao.blog.seven.dao.repository.TagRelationRepository;
import com.zhoutao.blog.seven.dao.repository.TagRepository;
import com.zhoutao.blog.seven.model.PageList;
import com.zhoutao.blog.seven.model.converter.ArticleConverter;
import com.zhoutao.blog.seven.model.detail.ArticleDetail;
import com.zhoutao.blog.seven.model.enums.RefType;
import com.zhoutao.blog.seven.model.response.Article;
import com.zhoutao.blog.seven.service.ArticleService;
import com.zhoutao.blog.seven.service.query.ArticleQuery;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ArticleServiceImpl implements ArticleService {

    private final ArticleConverter articleConverter;

    private final ArticleRepository articleRepository;

    private final BlogCategoryRepository categoryRepository;

    private final TagRelationRepository tagRelationRepository;

    private final TagRepository tagRepository;

    private final Cache<String, Map<Long, BlogCategoryDao>> CATEGORY_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES)
                    .build();

    @Override
    @SneakyThrows
    public PageList<ArticleDetail> find(ArticleQuery query, Integer page, Integer limit) {
        if (page == null || page <= 0) {
            page = 1;
        }

        Map<Long, BlogCategoryDao> categoryDaoMap = CATEGORY_CACHE.get("ALL", categoryRepository::mapGroupById);

        Optional<BlogCategoryDao> categoryDaoOptional = categoryRepository.findByName(query.getCategory());
        Optional<TagDao> tagDaoOptional = tagRepository.findByName(query.getTag());

        IPage<ArticleDao> daoPageList = articleRepository.all(
                categoryDaoOptional.map(BlogCategoryDao::getId),
                tagDaoOptional.map(TagDao::getId),
                Optional.ofNullable(query.getType()),
                page, limit);

        List<ArticleDetail> collect = daoPageList.getRecords().stream()
                .map(articleConverter::converterFrom)
                .map(ArticleDetail::new)
                .peek(detail ->
                        Optional.ofNullable(categoryDaoMap.get(detail.getCategoryId()))
                                .ifPresent(c -> {
                                    detail.setCategoryIcon(c.getIcon());
                                    detail.setCategoryTitle(c.getTitle());
                                }))
                .collect(Collectors.toList());

        return PageList.create(collect, page, limit, daoPageList.getTotal());

    }

    @Override
    @SneakyThrows
    public ArticleDetail findById(Long id) {
        if (id == null) {
            return null;
        }
        ArticleDao articleDao = articleRepository.getById(id);
        articleRepository.addReadCount(id);
        Article article = articleConverter.converterFrom(articleDao);
        ArticleDetail detail = new ArticleDetail(article);
        Map<Long, BlogCategoryDao> categoryDaoMap = CATEGORY_CACHE.get("ALL", categoryRepository::mapGroupById);
        Optional.ofNullable(categoryDaoMap.get(detail.getCategoryId()))
                .ifPresent(c -> {
                    detail.setCategoryIcon(c.getIcon());
                    detail.setCategoryTitle(c.getTitle());
                });
        return detail;
    }

    @Override
    @Transactional
    public Long save(Article article) {
        ArticleDao articleDao = articleConverter.converterTO(article);

        if (articleDao.getId() == null) {
            articleDao.setReadCount(0).setCommentCount(0);
            articleRepository.save(articleDao);
            log.info("创建文章完成:{}", articleDao.getTitle());
        } else {
            articleRepository.updateById(articleDao);
            log.info("更新文章完成:{}", articleDao.getTitle());
        }

        // 更新标签
        Long articleDaoId = articleDao.getId();
        tagRelationRepository.updateTagRelation(articleDaoId, RefType.ARTICLE, article.findTags());
        return articleDaoId;
    }
}
